'''
s7 客户端
pip install python-snap7
pip install python-snap7[cli]
https://blog.csdn.net/pista/article/details/113405915
启动模拟服务端命令 snap7-server
'''
import time
import traceback
try:
    from abc_client import PLCClientInterface # ./
except:
    try:
        from ..layer1.abc_client import PLCClientInterface # layer2/...
    except:
        from plc_client.layer1.abc_client import PLCClientInterface #
# 绝对地址通讯； 标签通讯方式

import modbus_tk.modbus_tcp as modbus_tcp
import modbus_tk.defines as cst

class MdsClient(PLCClientInterface):

    def __init__(self, ip='127.0.0.1', port=1102, dbnumber=183):
        # 创建通讯客户端实例
        # self.client = modbus_tcp.TcpMaster(host=ip, port=port, timeout_in_sec=3.0)
        self.client = None
        self.ip = ip
        self.port = port
        self.dbnumber = dbnumber

    def connect(self):
        # 连接至PLC
        try:
            # self.client.connect(self.ip, 0, 0, self.port)  # 请求大小为480B的pdu
            self.client = modbus_tcp.TcpMaster(host=self.ip, port=self.port, timeout_in_sec=3.0)
            print(self.client)
        except Exception as e:
            print('xxx')
            print(e)
        # print(f"客户端 是否连接到 plc服务端：{self.client.get_connected()}")

    def get_connected(self):
        # return self.client.get_connected()
        pass

    def read_int8(self, start=0, size=1):
        return self.plc_get16(start)

    def read_int16(self, start=0, size=2):  # 读int16
        return self.plc_get16(start)

    def read_bool(self, start=0, size=1): # 读bool
        pass

    def read_real(self, start=0, size=4): # 读实数
        pass


    def write_int8(self, start=0, data_int8=0): # 写int8
        self.plc_set16(start, data_int8)
        pass

    def write_int16(self, start=0, data_int8=0): #
        self.plc_set16(start, data_int8)

    def write_real(self, start=0, data_real=1.0): # 写实数  用于发送坐标等
        pass

    def write_bool(self, start=0, data_bool=False): # 写bool
        pass

    def read_string(self, start=0, size=50, dbnumber=0): # 读 string  size=256
        pass

    def write_string(self, start=0, data_str='hello', dbnumber=0): # write string
        pass


    def close(self):
        self.client.close()
        # print(f"plc 连接状态：{self.client.get_connected()}")

    # next layer
    def plc_set16(self, addr, a):
        '''

        :param addr: int 地址
        :param a: int 值
        :return:
        '''
        a0 = a&0xffff # 取前16位
        # plc_writes(addr, [a0, a1])
        #logger.info(f'plc_set16 {addr} {a}')
        self.plc_write(addr, [a0])
        #print(addr, plc_read(addr, 2))
        return 0

    def plc_get16(self, addr):
        '''

        :param addr:
        :return:
        '''
        a = self.plc_read(addr, 1)
        return a


    def plc_read(self, addr=0, bit_len=1):
        # print('plc_read', addr)
        for i in range(10):
            value = self._try_plc_read(addr, bit_len)
            if value is not None:
                if isinstance(value, tuple):
                    if len(value) == 1:
                        value = value[0]
                # print(f'plc_read {addr} == {value}')
                return value
            time.sleep(0.1)

        return -1

    def plc_write(self, addr, value):
        # print('plc_write', addr, value)
        for i in range(10):
            x = self._try_plc_write(addr, value)
            if x is not None:
                return x
            time.sleep(0.1)

        return -1

    def _try_plc_write(self, addr, value):
        try:
            if isinstance(addr, str):
                addr = int(addr)
            if isinstance(value, list): #
                salve_value = self.client.execute(1, cst.WRITE_MULTIPLE_REGISTERS, addr, output_value=value)
                return salve_value
            salve_value = self.client.execute(1, cst.WRITE_SINGLE_REGISTER, addr, output_value=value)
            # print("salve_value: ",salve_value)
            return salve_value
        except Exception as e:
            print(f'_try_plc_write {str(e)} {addr} {value}')
            print(traceback.format_exc())
            self.connect()
            return None


    def _try_plc_read(self, addr=0, bit_len=1):
        try:
            if isinstance(addr, str):
                addr = int(addr)
            # salve_value = melsec.execute(1, cst.READ_DISCRETE_INPUTS, addr, bit_len)
            # salve_value = melsec.execute(1, cst.READ_DISCRETE_INPUTS, addr, bit_len)
            # salve_value = melsec.execute(1, cst.WRITE_SINGLE_REGISTER, addr, 123)
            salve_value = self.client.execute(1, cst.READ_HOLDING_REGISTERS, addr, bit_len)
            return salve_value
        except Exception as e:
            print(f'_try_plc_read {str(e)} {addr} bit_len={bit_len}')
            print(traceback.format_exc())
            self.connect()
            return None



# def plc_set32(addr, a):
#     a0 = a&0xffff # 前16位
#     a1 = a>>16 # 后16位
#     # plc_writes(addr, [a0, a1])
#     plc_writes(addr, [a1, a0])
#     #print(addr, plc_read(addr, 2))
#     return 0
#
# def plc_get32(addr):
#     a = plc_read(addr, 2)
#     # pos = (a[1]<<16)+a[0]
#     pos = (a[0]<<16)+a[1]
#     return pos



def test0():
    pass
    mds =  MdsClient()
    value = mds.read_int16(start=0)
    print(value)
    value = mds.read_int8(start=0)
    print(value)
    mds.write_int8(start=0,  data_int8=10)
    mds.write_int16(start=0,  data_int8=10)



if __name__ == "__main__":
    test0()